use std::convert::TryFrom;
use std::fmt;

use rama_core::bytes::Bytes;
use rama_http_types::{HeaderName, HeaderValue};
use rama_net::address::{self, HostWithOptPort};

use crate::{Error, HeaderDecode, HeaderEncode, TypedHeader};

/// The `Host` header.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Host(pub HostWithOptPort);

impl TypedHeader for Host {
    fn name() -> &'static HeaderName {
        &::rama_http_types::header::HOST
    }
}

impl HeaderDecode for Host {
    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, Error> {
        let addr = values
            .next()
            .and_then(|val| HostWithOptPort::try_from(val.as_bytes()).ok())
            .ok_or_else(Error::invalid)?;
        Ok(Self(addr))
    }
}

impl HeaderEncode for Host {
    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
        let s = self.to_string();
        let bytes = Bytes::from_owner(s);
        let val = HeaderValue::from_maybe_shared(bytes).expect("Authority is a valid HeaderValue");

        values.extend(::std::iter::once(val));
    }
}

impl From<address::Host> for Host {
    #[inline(always)]
    fn from(host: address::Host) -> Self {
        Self(host.into())
    }
}

impl From<Host> for address::Host {
    #[inline(always)]
    fn from(value: Host) -> Self {
        value.0.host
    }
}

impl From<HostWithOptPort> for Host {
    #[inline(always)]
    fn from(addr: HostWithOptPort) -> Self {
        Self(addr)
    }
}

impl From<Host> for HostWithOptPort {
    #[inline(always)]
    fn from(host: Host) -> Self {
        host.0
    }
}

impl From<address::HostWithPort> for Host {
    #[inline(always)]
    fn from(addr: address::HostWithPort) -> Self {
        Self(addr.into())
    }
}

impl fmt::Display for Host {
    #[inline(always)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}
